//
// Created by Administrator on 2024/6/10 0010.
//

#include "RtspPlayer.h"
#include "log.h"
#include <unistd.h>

RtspPlayer::RtspPlayer() {
    avformat_network_init();
}

RtspPlayer::~RtspPlayer() {
    avformat_network_deinit();
}

bool RtspPlayer::initialize() {
    frame = av_frame_alloc();
    packet = av_packet_alloc();
    if (!frame || !packet) {
        LOGD("Failed to allocate frame or packet");
        return false;
    }
    return true;
}

void RtspPlayer::release() {
    if (swsContext) {
        sws_freeContext(swsContext);
        swsContext = nullptr;
    }
    if (codecContext) {
        avcodec_free_context(&codecContext);
        codecContext = nullptr;
    }
    if (formatContext) {
        avformat_close_input(&formatContext);
        formatContext = nullptr;
    }
    if (frame) {
        av_frame_free(&frame);
        frame = nullptr;
    }
    if (packet) {
        av_packet_free(&packet);
        packet = nullptr;
    }
}

bool RtspPlayer::openStream(const std::string &url) {
    //    release();
     // 检查路径是否有效
    if (url.c_str() == nullptr || strlen(url.c_str()) == 0) {
        LOGD("Invalid video path");
        return false;
    }
    LOGD("Video path: %s", url.c_str());
    if (!(formatContext = avformat_alloc_context())) {
        LOGE("has no enough memory!");
        return -1;
    }
    LOGD("avformat_open_input");
    if (avformat_open_input(&formatContext, url.c_str(), NULL, NULL) != 0) {
        LOGD("Failed to open input stream");
        return false;
    }
    LOGD("avformat_find_stream_info");
    if (avformat_find_stream_info(formatContext, NULL) < 0) {
        LOGD("Failed to find stream info");
        return false;
    }

    videoStreamIndex = -1;
    for (unsigned int i = 0; i < formatContext->nb_streams; ++i) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }
    if (videoStreamIndex == -1) {
        LOGD("Failed to find video stream");
        return false;
    }
    LOGD("avcodec_find_decoder");
    AVCodecParameters *codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
    const AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);
    if (!codec) {
        LOGD("Failed to find decoder");
        return false;
    }

    codecContext = avcodec_alloc_context3(codec);
    if (!codecContext) {
        LOGD("Failed to allocate codec context");
        return false;
    }

    if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
        LOGD("Failed to copy codec parameters to context");
        return false;
    }

    if (avcodec_open2(codecContext, codec, NULL) < 0) {
        LOGD("Failed to open codec");
        return false;
    }

    LOGD("initialize");

    return initialize();
}

void RtspPlayer::decodeAndPlay(JNIEnv *env, jobject surface) {
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
    ANativeWindow_setBuffersGeometry(nativeWindow, codecContext->width, codecContext->height, WINDOW_FORMAT_RGBA_8888);

    LOGD("decodeAndPlay");
    while (av_read_frame(formatContext, packet) >= 0) {
        LOGD("decodeAndPlay av_read_frame success");
        if (packet->stream_index == videoStreamIndex) {
            int ret = avcodec_send_packet(codecContext, packet);
            if (ret < 0) {
                LOGD("Error sending packet for decoding");
                break;
            }

            while (ret >= 0) {
                ret = avcodec_receive_frame(codecContext, frame);
                LOGD("decodeAndPlay avcodec_receive_frame ret=%d",ret);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    break;
                else if (ret < 0) {
                    LOGD("Error during decoding");
                    break;
                }

                ANativeWindow_Buffer windowBuffer;
                if (ANativeWindow_lock(nativeWindow, &windowBuffer, nullptr) < 0) {
                    LOGD("Cannot lock window");
                } else {
                    swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,
                                                codecContext->width, codecContext->height, AV_PIX_FMT_RGBA, SWS_BILINEAR,
                                                nullptr, nullptr, nullptr);

                    uint8_t *dst[4] = {static_cast<uint8_t *>(windowBuffer.bits)};
                    int dstStride[4] = {windowBuffer.stride * 4};
                    LOGD("lock window pts:%lld",frame->pkt_dts);
                    sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, dst, dstStride);

                    ANativeWindow_unlockAndPost(nativeWindow);
                }
            }
        }
        av_packet_unref(packet);
    }
    LOGD("decodeAndPlay finish");
    ANativeWindow_release(nativeWindow);
}
